#define DEBUG
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using ICSharpCode.TextEditor.Util;

namespace ICSharpCode.TextEditor.Document
{
	internal sealed class LineSegmentTree : IList<LineSegment>, ICollection<LineSegment>, IEnumerable<LineSegment>, IEnumerable
	{
		internal struct RBNode
		{
			internal LineSegment lineSegment;

			internal int count;

			internal int totalLength;

			public RBNode(LineSegment lineSegment)
			{
				this.lineSegment = lineSegment;
				this.count = 1;
				this.totalLength = lineSegment.TotalLength;
			}

			public override string ToString()
			{
				return "[RBNode count=" + this.count + " totalLength=" + this.totalLength + " lineSegment.LineNumber=" + this.lineSegment.LineNumber + " lineSegment.Offset=" + this.lineSegment.Offset + " lineSegment.TotalLength=" + this.lineSegment.TotalLength + " lineSegment.DelimiterLength=" + this.lineSegment.DelimiterLength + "]";
			}
		}

		[StructLayout(LayoutKind.Sequential, Size = 1)]
		private struct MyHost : IRedBlackTreeHost<RBNode>, IComparer<RBNode>
		{
			public int Compare(RBNode x, RBNode y)
			{
				throw new NotImplementedException();
			}

			public bool Equals(RBNode a, RBNode b)
			{
				throw new NotImplementedException();
			}

			public void UpdateAfterChildrenChange(RedBlackTreeNode<RBNode> node)
			{
				int num = 1;
				int num2 = node.val.lineSegment.TotalLength;
				if (node.left != null)
				{
					num += node.left.val.count;
					num2 += node.left.val.totalLength;
				}
				if (node.right != null)
				{
					num += node.right.val.count;
					num2 += node.right.val.totalLength;
				}
				if (num != node.val.count || num2 != node.val.totalLength)
				{
					node.val.count = num;
					node.val.totalLength = num2;
					if (node.parent != null)
					{
						this.UpdateAfterChildrenChange(node.parent);
					}
				}
			}

			public void UpdateAfterRotateLeft(RedBlackTreeNode<RBNode> node)
			{
				this.UpdateAfterChildrenChange(node);
				this.UpdateAfterChildrenChange(node.parent);
			}

			public void UpdateAfterRotateRight(RedBlackTreeNode<RBNode> node)
			{
				this.UpdateAfterChildrenChange(node);
				this.UpdateAfterChildrenChange(node.parent);
			}
		}

		public struct Enumerator : IEnumerator<LineSegment>, IEnumerator, IDisposable
		{
			public static readonly Enumerator Invalid;

			internal RedBlackTreeIterator<RBNode> it;

			public LineSegment Current => this.it.Current.lineSegment;

			public bool IsValid => this.it.IsValid;

			public int CurrentIndex
			{
				get
				{
					if (this.it.node == null)
					{
						throw new InvalidOperationException();
					}
					return LineSegmentTree.GetIndexFromNode(this.it.node);
				}
			}

			public int CurrentOffset
			{
				get
				{
					if (this.it.node == null)
					{
						throw new InvalidOperationException();
					}
					return LineSegmentTree.GetOffsetFromNode(this.it.node);
				}
			}

			object IEnumerator.Current => this.it.Current.lineSegment;

			internal Enumerator(RedBlackTreeIterator<RBNode> it)
			{
				this.it = it;
			}

			public void Dispose()
			{
			}

			public bool MoveNext()
			{
				return this.it.MoveNext();
			}

			public bool MoveBack()
			{
				return this.it.MoveBack();
			}

			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private readonly AugmentableRedBlackTree<RBNode, MyHost> tree = new AugmentableRedBlackTree<RBNode, MyHost>(default(MyHost));

		public int TotalLength
		{
			get
			{
				if (this.tree.root == null)
				{
					return 0;
				}
				return this.tree.root.val.totalLength;
			}
		}

		public int Count => this.tree.Count;

		public LineSegment this[int index]
		{
			get
			{
				return this.GetNode(index).val.lineSegment;
			}
			set
			{
				throw new NotSupportedException();
			}
		}

		bool ICollection<LineSegment>.IsReadOnly => true;

		private RedBlackTreeNode<RBNode> GetNode(int index)
		{
			if (index < 0 || index >= this.tree.Count)
			{
				throw new ArgumentOutOfRangeException("index", index, "index should be between 0 and " + (this.tree.Count - 1));
			}
			RedBlackTreeNode<RBNode> redBlackTreeNode = this.tree.root;
			while (true)
			{
				if (redBlackTreeNode.left != null && index < redBlackTreeNode.left.val.count)
				{
					redBlackTreeNode = redBlackTreeNode.left;
					continue;
				}
				if (redBlackTreeNode.left != null)
				{
					index -= redBlackTreeNode.left.val.count;
				}
				if (index == 0)
				{
					break;
				}
				index--;
				redBlackTreeNode = redBlackTreeNode.right;
			}
			return redBlackTreeNode;
		}

		private static int GetIndexFromNode(RedBlackTreeNode<RBNode> node)
		{
			int num = ((node.left != null) ? node.left.val.count : 0);
			while (node.parent != null)
			{
				if (node == node.parent.right)
				{
					if (node.parent.left != null)
					{
						num += node.parent.left.val.count;
					}
					num++;
				}
				node = node.parent;
			}
			return num;
		}

		private RedBlackTreeNode<RBNode> GetNodeByOffset(int offset)
		{
			if (offset < 0 || offset > this.TotalLength)
			{
				throw new ArgumentOutOfRangeException("offset", offset, "offset should be between 0 and " + this.TotalLength);
			}
			if (offset == this.TotalLength)
			{
				if (this.tree.root == null)
				{
					throw new InvalidOperationException("Cannot call GetNodeByOffset while tree is empty.");
				}
				return this.tree.root.RightMost;
			}
			RedBlackTreeNode<RBNode> redBlackTreeNode = this.tree.root;
			while (true)
			{
				if (redBlackTreeNode.left != null && offset < redBlackTreeNode.left.val.totalLength)
				{
					redBlackTreeNode = redBlackTreeNode.left;
					continue;
				}
				if (redBlackTreeNode.left != null)
				{
					offset -= redBlackTreeNode.left.val.totalLength;
				}
				offset -= redBlackTreeNode.val.lineSegment.TotalLength;
				if (offset < 0)
				{
					break;
				}
				redBlackTreeNode = redBlackTreeNode.right;
			}
			return redBlackTreeNode;
		}

		private static int GetOffsetFromNode(RedBlackTreeNode<RBNode> node)
		{
			int num = ((node.left != null) ? node.left.val.totalLength : 0);
			while (node.parent != null)
			{
				if (node == node.parent.right)
				{
					if (node.parent.left != null)
					{
						num += node.parent.left.val.totalLength;
					}
					num += node.parent.val.lineSegment.TotalLength;
				}
				node = node.parent;
			}
			return num;
		}

		public LineSegment GetByOffset(int offset)
		{
			return this.GetNodeByOffset(offset).val.lineSegment;
		}

		public void SetSegmentLength(LineSegment segment, int newTotalLength)
		{
			if (segment == null)
			{
				throw new ArgumentNullException("segment");
			}
			RedBlackTreeNode<RBNode> node = segment.treeEntry.it.node;
			segment.TotalLength = newTotalLength;
			MyHost myHost = default(MyHost);
			myHost.UpdateAfterChildrenChange(node);
		}

		public void RemoveSegment(LineSegment segment)
		{
			this.tree.RemoveAt(segment.treeEntry.it);
		}

		public LineSegment InsertSegmentAfter(LineSegment segment, int length)
		{
			LineSegment lineSegment = new LineSegment();
			lineSegment.TotalLength = length;
			lineSegment.DelimiterLength = segment.DelimiterLength;
			lineSegment.treeEntry = this.InsertAfter(segment.treeEntry.it.node, lineSegment);
			return lineSegment;
		}

		private Enumerator InsertAfter(RedBlackTreeNode<RBNode> node, LineSegment newSegment)
		{
			RedBlackTreeNode<RBNode> redBlackTreeNode = new RedBlackTreeNode<RBNode>(new RBNode(newSegment));
			if (node.right == null)
			{
				this.tree.InsertAsRight(node, redBlackTreeNode);
			}
			else
			{
				this.tree.InsertAsLeft(node.right.LeftMost, redBlackTreeNode);
			}
			return new Enumerator(new RedBlackTreeIterator<RBNode>(redBlackTreeNode));
		}

		public int IndexOf(LineSegment item)
		{
			int lineNumber = item.LineNumber;
			if (lineNumber < 0 || lineNumber >= this.Count)
			{
				return -1;
			}
			if (item != this[lineNumber])
			{
				return -1;
			}
			return lineNumber;
		}

		void IList<LineSegment>.RemoveAt(int index)
		{
			throw new NotSupportedException();
		}

		[Conditional("DATACONSISTENCYTEST")]
		private void CheckProperties()
		{
			if (this.tree.root == null)
			{
				Debug.Assert(this.Count == 0);
				return;
			}
			Debug.Assert(this.tree.root.val.count == this.Count);
			this.CheckProperties(this.tree.root);
		}

		private void CheckProperties(RedBlackTreeNode<RBNode> node)
		{
			int num = 1;
			int num2 = node.val.lineSegment.TotalLength;
			if (node.left != null)
			{
				this.CheckProperties(node.left);
				num += node.left.val.count;
				num2 += node.left.val.totalLength;
			}
			if (node.right != null)
			{
				this.CheckProperties(node.right);
				num += node.right.val.count;
				num2 += node.right.val.totalLength;
			}
			Debug.Assert(node.val.count == num);
			Debug.Assert(node.val.totalLength == num2);
		}

		public string GetTreeAsString()
		{
			return this.tree.GetTreeAsString();
		}

		public LineSegmentTree()
		{
			this.Clear();
		}

		public void Clear()
		{
			this.tree.Clear();
			LineSegment lineSegment = new LineSegment();
			lineSegment.TotalLength = 0;
			lineSegment.DelimiterLength = 0;
			this.tree.Add(new RBNode(lineSegment));
			lineSegment.treeEntry = this.GetEnumeratorForIndex(0);
		}

		public bool Contains(LineSegment item)
		{
			return this.IndexOf(item) >= 0;
		}

		public void CopyTo(LineSegment[] array, int arrayIndex)
		{
			if (array == null)
			{
				throw new ArgumentNullException("array");
			}
			foreach (LineSegment item in this)
			{
				array[arrayIndex++] = item;
			}
		}

		IEnumerator<LineSegment> IEnumerable<LineSegment>.GetEnumerator()
		{
			return this.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return this.GetEnumerator();
		}

		public Enumerator GetEnumerator()
		{
			return new Enumerator(this.tree.GetEnumerator());
		}

		public Enumerator GetEnumeratorForIndex(int index)
		{
			return new Enumerator(new RedBlackTreeIterator<RBNode>(this.GetNode(index)));
		}

		public Enumerator GetEnumeratorForOffset(int offset)
		{
			return new Enumerator(new RedBlackTreeIterator<RBNode>(this.GetNodeByOffset(offset)));
		}

		void IList<LineSegment>.Insert(int index, LineSegment item)
		{
			throw new NotSupportedException();
		}

		void ICollection<LineSegment>.Add(LineSegment item)
		{
			throw new NotSupportedException();
		}

		bool ICollection<LineSegment>.Remove(LineSegment item)
		{
			throw new NotSupportedException();
		}
	}
}
